package com.canyou.datascope;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * mybatis plus 数据权限插件.
 * 针对不同的登录用户所能看到的 公司 数据是不一样的
 * 在需要做数据权限的Mapper方法上加上 DataScope 参数即可
 *
 * @author fy
 * @date 2018-08-08 16:59
 */
@Component
@Intercepts({@Signature(type = StatementHandler.class,
        method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        /// this.sqlParser(metaObject);

        // 先判断是不是SELECT操作
        MappedStatement mappedStatement = (MappedStatement) metaObject
                .getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }

        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        String originalSql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();

        // 查找参数中包含DataScope类型的参数
        DataScope dataScope = findDataScopeObject(parameterObject);

        if (dataScope == null) {
            return invocation.proceed();
        } else {
            final String scopeName = dataScope.getScopeName();
            final List<Long> scopeIds = dataScope.getScopeIds();
            final boolean optimize = dataScope.isOptimize();
            if (StringUtils.isNotBlank(scopeName) && scopeIds != null) {
                if (!optimize) {
                    String join = scopeIds.stream().map(Object::toString).collect(Collectors.joining(","));
                    // 该语句适用于所有查询
                    originalSql = "SELECT * FROM (" + originalSql + ") TEMP_DATA_SCOPE WHERE TEMP_DATA_SCOPE." + scopeName;
                    if (scopeIds.isEmpty()) {
                        originalSql += " = 0";
                    } else if (scopeIds.size() == 1) {
                        originalSql += " = " + join;
                    } else {
                        originalSql += " IN (" + join + ")";
                    }
                    metaObject.setValue("delegate.boundSql.sql", originalSql);
                } else {
                    // 使用SqlParser解析, 添加where条件
                    metaObject.setValue("delegate.boundSql.sql", new DataScopeSqlParser(scopeName, scopeIds)
                            .parser(metaObject, originalSql).getSql());
                }
            }
            return invocation.proceed();
        }
    }

    /**
     * 生成拦截对象的代理
     *
     * @param target 目标对象
     * @return 代理对象
     */
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    /**
     * 查找参数是否包括DataScope对象
     *
     * @param parameterObj 参数列表
     * @return DataScope
     */
    private DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }

}
